﻿using Cyss.Core.Cache;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cyss.Core.Api.Filters
{
    /// <summary>
    /// 分布式缓存锁
    /// </summary>
    public class ApiLockAttribute : Attribute, IAsyncActionFilter
    {
        /// <summary>
        /// 锁key
        /// </summary>
        public string Key { set; get; }

        /// <summary>
        /// 是否等待
        /// </summary>
        public bool IsWait { set; get; }

        /// <summary>
        /// 到期时间秒
        /// </summary>
        private int Expiry { set; get; } = 120;

        /// <summary>
        /// 是否是参数级别锁
        /// </summary>
        public bool IsParameterLevel { set; get; }

        /// <summary>
        /// 提示
        /// </summary>
        public string Title { set; get; } = "操作有人正在执行请稍后在试";

        /// <summary>
        /// 
        /// </summary>
        private IStaticCacheManager _staticCacheManager { set; get; }

        public ApiLockAttribute()
        {

        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (_staticCacheManager==null)
            {
                _staticCacheManager=IOCEngine.Resolve<IStaticCacheManager>();
            }

            if (string.IsNullOrWhiteSpace(this.Key))
            {
                this.Key = context.RouteData.Values["controller"].ToString()+"-"+context.RouteData.Values["action"].ToString();
            }
            string key = "ApiLock:"+this.Key;
            string token = Guid.NewGuid().ToString();
            if (this.IsParameterLevel)
            {
                key=key+"-par:"+GetFormatParameters(context);
            }
            var islock = _staticCacheManager.LockTake(key, token, Expiry);
            if (islock==false && IsWait)
            {
                islock = _staticCacheManager.LockTake(key, token, Expiry);
                while (islock==false)
                {
                    await Task.Delay(100);
                    islock = _staticCacheManager.LockTake(key, token, Expiry);
                }
            }

            if (islock)
            {
                await next();
                _staticCacheManager.LockRelease(key, token);
            }
            else
            {
                OperateResult apiResponse = new OperateResult();
                apiResponse.Code = 100;
                apiResponse.Message =this.Title;
                context.Result = new JsonResult(apiResponse);
                return;
            }
        }

        /// <summary>
        /// 格式化参数
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public string GetFormatParameters(ActionExecutingContext context)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var item in context.ActionArguments)
            {
                stringBuilder.Append(item.Key+":"+item.Value.ToSerializeObject());
            }
            if (context.HttpContext!=null && context.HttpContext.Request!=null && context.HttpContext.Request.QueryString.HasValue)
            {
                stringBuilder.Append(context.HttpContext.Request.QueryString.Value);
            }
            return EncryptHelper.MD5Encrypt(stringBuilder.ToString());
        }

    }
}
